using System;
using System.Text;
using System.IO;
using FamilyTreeProject.GEDCOM.Records;
using FamilyTreeProject.GEDCOM.Common;

namespace FamilyTreeProject.GEDCOM.IO
{
    public class GEDCOMWriter
    {
        #region Private Members

        TextWriter writer;
        int indent = 2;

        #endregion

        #region Constructors

        /// <summary>
        /// This constructor creates a GEDCOMWriter from a TextWriter
        /// </summary>
        /// <param name="reader">The TextWriter to use</param>
        private GEDCOMWriter(TextWriter writer)
        {
            this.writer = writer;
        }

        /// <summary>
        /// This constructor creates a GEDCOMWriter that writes to a Stream
        /// </summary>
        /// <param name="stream">The Stream to use</param>
        private GEDCOMWriter(Stream stream)
        {
            this.writer = new StreamWriter(stream);
        }

        /// <summary>
        /// This constructor creates a GEDCOMWriter that writes to a String
        /// </summary>
        /// <param name="text">The String to use</param>
        private GEDCOMWriter(StringBuilder sb)
        {
            this.writer = new StringWriter(sb);
        }

        #endregion    

        public string NewLine
        {
            get { return writer.NewLine;  }
            set { writer.NewLine = value; }
        }

        #region Private Methods

        private void WriteSpace()
        {
            WriteText(" ");
        }

        private void WriteText(string text)
        {
            writer.Write(text);
        }

        #endregion

        #region Public Static Methods

        /// <summary>
        /// Creates a GEDCOMWriter from a TextReader
        /// </summary>
        /// <param name="reader">The TextReader to use</param>
        public static GEDCOMWriter Create(TextWriter writer)
        {
            if (writer == null)
                throw new ArgumentNullException("writer");
            return new GEDCOMWriter(writer);
        }

        /// <summary>
        /// Creates a GEDCOMWriter that writes to a Stream
        /// </summary>
        /// <param name="stream">The Stream to use</param>
        public static GEDCOMWriter Create(Stream stream)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");
            return new GEDCOMWriter(stream);
        }

        /// <summary>
        /// Creates a GEDCOMWriter that writes to a String
        /// </summary>
        /// <param name="stringBuilder">The StringBuilder to use</param>
        public static GEDCOMWriter Create(StringBuilder stringBuilder)
        {
            if (stringBuilder == null)
                throw new ArgumentNullException("stringBuilder");
            return new GEDCOMWriter(stringBuilder);
        }

        #endregion

        public void Close()
        {
            if (writer != null)
            {
                writer.Flush();
                writer.Close();
            }
        }

        #region WriteData Methods

        public void WriteData(string data)
        {
            WriteText(data);
        }

        #endregion

        #region WriteId Methods

        public void WriteId(string xRefId)
        {
            WriteText(xRefId);
        }

        public void WriteId(int Id, string prefix)
        {
            WriteText("@" + prefix + Id.ToString() + "@");
        }

        #endregion

        #region WriteLevel Methods

        public void WriteLevel(int level)
        {
            WriteText(level.ToString());
        }

        #endregion

        public void WriteNewLine()
        {
            writer.Write(NewLine);
        }

        #region WriteRecord Methods

        public void WriteRecord(GEDCOMRecord record)
        {
            WriteRecord(record, true);
        }

        public void WriteRecord(GEDCOMRecord record, bool includeChildRecords)
        {
            //Split the data into CONT
            record.SplitData(); 

            WriteRecord(record.Id, record.Level, record.XRefId, record.Tag, record.Data);

            if (includeChildRecords)
            {
                WriteRecords(record.ChildRecords, includeChildRecords);
            }
        }

        public void WriteRecord(string id, int level, string xRefId, string tag, string data)
        {
            //Write the level
            WriteLevel(level);

            //If the id is specified tag is of the form "level id tag"
            //If the XRefId is specified record is of form "level tag xRefId"
            //Else record is of form "level tag data"
            if (!String.IsNullOrEmpty(id))
            {
                //Level Id Tag
                WriteSpace();
                WriteId(id);

                if (!String.IsNullOrEmpty(tag))
                {
                    WriteSpace();
                    WriteTag(tag);
                }
            }
            else
                if (!String.IsNullOrEmpty(xRefId))
                {
                    //Level Tag XRefID
                    if (!String.IsNullOrEmpty(tag))
                    {
                        WriteSpace();
                        WriteTag(tag);
                    }
                    if (!String.IsNullOrEmpty(xRefId))
                    {
                        WriteSpace();
                        WriteXRefId(xRefId);
                    }
                }
                else
                {
                    //Level Tag Data
                    if (!String.IsNullOrEmpty(tag))
                    {
                        WriteSpace();
                        WriteTag(tag);
                    }
                    if (!String.IsNullOrEmpty(data))
                    {
                        WriteSpace();
                        WriteData(data);
                    }
                }
            WriteNewLine();
        }

        #endregion

        public void WriteRecords(GEDCOMRecordList records)
        {
            WriteRecords(records, true);
        }

        public void WriteRecords(GEDCOMRecordList records, bool includeChildRecords)
        {
            foreach (GEDCOMRecord record in records)
            {
                WriteRecord(record, includeChildRecords);
            }
        }

        #region WriteTag Methods

        public void WriteTag(GEDCOMTag tagName)
        {
            WriteTag(tagName.ToString());
        }

        public void WriteTag(string tag)
        {
            WriteText(tag);
        }

        #endregion

        #region WriteXRefID Methods

        public void WriteXRefId(string xRefId)
        {
            WriteText(xRefId);
        }

        public void WriteXRefId(int xRefId, string prefix)
        {
            WriteText("@" + prefix + xRefId.ToString() + "@");
        }

        #endregion
    }
}
